package com.iot.ammeter.network

import com.iot.ammeter.IApplication
import com.iot.ammeter.network.mock.AuthMock
import com.iot.ammeter.network.mock.BaseDataMock
import com.iot.ammeter.network.services.AuthService
import com.iot.ammeter.network.services.BaseDataService
import com.iot.ammeter.network.services.RetrofitService
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.mock.BehaviorDelegate
import retrofit2.mock.MockRetrofit
import retrofit2.mock.NetworkBehavior
import java.lang.RuntimeException
import java.lang.reflect.Constructor
import java.util.concurrent.TimeUnit

/**
 * Retrofit Service API 接口构建者
 * 根据 [isMock] 来决定是否创建 mock service
 */
object ServiceCreator {

    // 是否使用 mockRetrofit 拦截请求模拟返回数据
    private val isMock get() = IApplication.isDebugEnv

    private val retrofit: Retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    // mock 网络行为，采用默认配置
    private val behavior = NetworkBehavior.create().apply {
        setDelay(200, TimeUnit.MILLISECONDS) // 5s 太久了, 2s delay
        setFailurePercent(0) // network calls will fail. 这个是网络请求失败抛出 MockRetrofitIOException
        setErrorPercent(2) // network calls will return errors. 这个会导致 HTTP 请求失败抛出 HttpException
    }

    private val mockRetrofit: MockRetrofit?
        get() =
            if (isMock) MockRetrofit.Builder(retrofit).networkBehavior(behavior).build() else null

    inline fun <reified T : RetrofitService> create(): T = createInstance(T::class.java)

    /**
     * 创建对应接口实例，非 Mock 状态时由 Retrofit 创建实例，
     * 当 [isMock] 为 true 时获取对应的 mock 类实例
     * @suppress 忽略泛型转换检测警告
     */
    @Suppress("UNCHECKED_CAST")
    fun <T : RetrofitService> createInstance(serviceClass: Class<T>): T = if (isMock) {
        val delegate = mockRetrofit!!.create(serviceClass)
        val constructor = getMockClassConstructor(serviceClass)
        val mockInstance = constructor.newInstance(delegate)
        mockInstance as T
    } else retrofit.create(serviceClass)

    /**
     * 根据 Retrofit Service 接口名字获取对应的 Mock 子类名，并加载该 Class 对象返回单参数构造对象
     */
    private fun <T : RetrofitService> getMockClassConstructor(serviceClass: Class<T>):
            Constructor<*> = when (serviceClass) {
        BaseDataService::class.java -> BaseDataMock::class.java
        AuthService::class.java -> AuthMock::class.java
        else -> throw RuntimeException("==== Not Found Service Mock Class ====")
    }.getConstructor(BehaviorDelegate::class.java)
}